﻿using System;
using System.Threading;
using log4net;

namespace Interactive.Utility.Collections {
    public sealed class ObjectPool<T> : IDisposable {
        private static readonly ILog log = LogManager.GetLogger(typeof (ObjectPool<T>));


        /// <summary>
        ///   对象缓冲为命中时的创建回调方法
        /// </summary>
        private readonly Func<T> _creator;

        /// <summary>
        /// 锁定时的锁
        /// </summary>
        private readonly object _myLock = new object();

        /// <summary>
        ///   对象缓冲池的容量
        /// </summary>
        private readonly int _poolSize;

        private readonly LockFreeStack<T> _stack;

        /// <summary>
        ///   对象创建次数
        /// </summary>
        private int _createCount;

        /// <summary>
        ///   堆栈中当前存在的对象个数
        /// </summary>
        private int _currentSize;

        /// <summary>
        ///   获取对象时缓存命中次数
        /// </summary>
        private int _hitCount;

        /// <summary>
        ///   对象回收失败次数
        /// </summary>
        private int _recyleFailedCount;

        /// <summary>
        ///   对象回收成功次数
        /// </summary>
        private int _recyleSuccessCount;

        public ObjectPool(int minPoolSize, int maxPoolSize, Func<T> creator) {
            if (minPoolSize <= 0)
                throw new ArgumentOutOfRangeException("minPoolSize", maxPoolSize, "minPoolSize必须大于0");
            if (maxPoolSize <= 0)
                throw new ArgumentOutOfRangeException("maxPoolSize", maxPoolSize, "maxPoolSize必须大于0");
            if (creator == null)
                throw new ArgumentNullException("creator");

            _poolSize = maxPoolSize;
            _creator = creator;
            _stack = new LockFreeStack<T>();
            _createCount = minPoolSize;
            for (int i = 0; i < _createCount; i++) Set(creator());
        }

        #region IDisposable Members

        public void Dispose() {
            if (!(typeof (T) is IDisposable)) return;
            lock (_myLock) {
                T item;
                while (_stack.TryPop(out item)) {
                    ((IDisposable) item).Dispose();
                }
                log.Info("在释放资源时,数据如下:currentSize:" + _currentSize + " createCount:" + _createCount + " hitCount:" +
                         _hitCount + " recyleFailedCount:" + _recyleFailedCount + " recyleSuccessCount:" +
                         _recyleSuccessCount);
                _currentSize = 0;
            }
        }

        #endregion

        public T Get() {
            T item;
            if (_stack.TryPop(out item)) {
                Interlocked.Decrement(ref _currentSize);
                Interlocked.Increment(ref _hitCount);
                return item;
            }
            Interlocked.Increment(ref _createCount);
            return _creator();
        }

        public bool Set(T args) {
            if (Interlocked.Increment(ref _currentSize) < _poolSize) {
                Interlocked.Increment(ref _recyleSuccessCount);
                _stack.Push(args);
                return true;
            }
            Interlocked.Increment(ref _recyleFailedCount);
            Interlocked.Decrement(ref _currentSize);
            return false;
        }
    }
}